#pragma once
#include<vulkan/vulkan.hpp>
#include"QueueFamilyIndices.h"
#include<iostream>
#include"SwapChainSupportDetails.hpp"
#include<string>
#include"SyncObject.hpp"
#include"Vertex.hpp"
#include<glm/glm.hpp>

namespace VKG{

    
    class VKGraphics
    {
    private:
        /* data */
        vk::Instance instance;
        vk::DebugReportCallbackEXT callback;
        vk::PhysicalDevice physicaldevice;
        vk::Device device;
        vk::Queue graphicsQueure;
        vk::Queue presentQueue;
        vk::SurfaceKHR surface;
        vk::SwapchainKHR swapchain;
        std::vector<vk::Image> swapchainImages;
        vk::Extent2D swapExent;
        vk::SurfaceFormatKHR swapformat;
        std::vector<vk::ImageView>  swapImageViews;
        vk::RenderPass renderPass;
        vk::PipelineLayout pipelineLayout;
        vk::Pipeline graphicPipeline;
        std::vector<vk::Framebuffer> swapchainFrameBuffers;
        vk::CommandPool commandpool;
        std::vector<vk::CommandBuffer> commandBuffers;
        vk::Semaphore imageAvailableSemaphore;
        vk::Semaphore renderfinishedSemaphore;
        vk::Buffer vertexBuffer;
        vk::DeviceMemory vertexBufferMemory;
        vk::Buffer indexBuffer;
        vk::DeviceMemory indexBufferMemory;
        vk::DescriptorSetLayout descriptorLayout;
        vk::Buffer uniformBuffer;
        vk::DeviceMemory uniformBufferMemory;
        vk::DescriptorSet descriptorset;
        vk::DescriptorPool descirptorPool;

        std::vector< const char*>  _externs;
        SwapChainSupportDetails swapchainsupport;

        const std::vector<const char*> validationLayers={"VK_LAYER_KHRONOS_validation"};
        const std::vector<const char*> deviceExtensions={VK_KHR_SWAPCHAIN_EXTENSION_NAME};
        const std::vector<Vertex> verteces = {
            {{-0.5f,-0.5f},{1.0f,0.0f,0.0f}},
            {{0.5f,-0.5f},{0.0f,1.0f,0.0f}},
            {{0.5f,0.5f},{0.0f,0.0f,1.0f}},
            {{-0.5f,0.5f},{1.0f,1.0f,1.0f}}
        };
        const std::vector<uint16_t> indices{
            0,1,2,2,3,0
        };

        const int MAX_FRAMS_FLIGHT = 2;
        std::vector<SyncObject> SyncObjects;
        #ifdef NDEBUG
        const bool enableValidationLayers = false;
        #else
        const bool enableValidationLayers = true;
        #endif

        struct uniformBufferObj{
            glm::mat4 model;
            glm::mat4 view;
            glm::mat4 proj;
        }ubo;
    public:
        VKGraphics(/* args */);
        ~VKGraphics();
        template<class T>
        void init(const char *externs[],int count,T);
        void draw();
        void wait();
        void undateUBO();
    private:
        void distroy();
        void CreateInstance();
        void setupDebugCallback();
        void pickPhysicalDevice();
        void createLogicDevice();
        void createSwapChain();
        void createImageViews();
        void createRanderpass();
        void createGraphicsPipline();
        void createSwapchainFrameBuffers();
        void createCommandPool();
        void createCommandBuffers();
        void createSemaphores();
        void drawFrame();
        void recordCommandBuffer(vk::CommandBuffer commandBuffer,int32_t imageindex);
        void createVertexBuffer();
        void createIndexBuffer();
        void createDescriptorSetLayout();
        
        void createUinformBuffer();
        void createDescriptorSet();



        bool checkValidationLayersSupport();
        bool checkDeviceExtensionSupport(vk::PhysicalDevice device);
        bool isDeviceSuitable(vk::PhysicalDevice);
        SwapChainSupportDetails querySwapchainSupport(vk::PhysicalDevice);
        QueueFamilyIndices findQueueFamilies(vk::PhysicalDevice);
        std::vector<const char*> getRequiredExtensions();
        std::vector< char> readFile(const std::string& path);
        vk::ShaderModule createShaderModule(const std::string& path);
        uint32_t findMemoryType(uint32_t typefilter,vk::MemoryPropertyFlags properties);

        vk::Buffer createBuffer(vk::DeviceSize dataSize,vk::BufferUsageFlags usages,vk::MemoryPropertyFlags memoryProperties ,vk::DeviceMemory&);
        void copyBuffer(vk::Buffer srcBuffer,vk::Buffer dstBuffer,vk::DeviceSize bufferSize);
        
        vk::Buffer transBuffer(const void*src,vk::DeviceSize dataSize,vk::BufferUsageFlags usages,vk::DeviceMemory&);
       
    };


    template<class T>
    void VKGraphics::init(const char *externs[],int count,T func )
    {
        for (size_t i = 0; i < count; i++)
        {
            _externs .push_back(externs[i]);
        }
        //创建instance
        CreateInstance();
        // 2 创建surface
        VkInstance vkinstance = instance;
        VkSurfaceKHR vksurface = func(vkinstance);
        surface = vksurface;
        if (!surface)
        {
            std::cerr<<"创建surface失败"<<std::endl;
        }
        //3 选择显卡设备
        pickPhysicalDevice();
        // 4 创建虚拟显卡 ,同时创建 消息队列
        createLogicDevice();
        // 创建交换链
        createSwapChain();
        //创建ImageView
        createImageViews();
        //创建渲染通道
        createRanderpass();
        //创建描述符布局
        createDescriptorSetLayout();
        //创建图形管线
        createGraphicsPipline();
        //帧缓冲区
        createSwapchainFrameBuffers();
        //命令池
        createCommandPool();
        //创建顶点缓冲区
        createVertexBuffer();
        //创建索引缓冲区
        createIndexBuffer(); 
        //创建描述符集
        createDescriptorSet();
        //命令缓冲区
        createCommandBuffers();
        //创建信号量
        createSemaphores();
    }
}
